package cn.yilongquan.util;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 随机字符串生成工具类
 * 支持生成包含字母、数字、特殊符号的随机字符串
 */
public class RandomStringUtils {

    // 字符常量定义
    private static final String UPPER_CASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String LOWER_CASE = "abcdefghijklmnopqrstuvwxyz";
    private static final String DIGITS = "0123456789";
    private static final String SPECIAL_CHARS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
    
    // 使用安全的随机数生成器
    private static final Random RANDOM = new SecureRandom();

    /**
     * 生成指定长度的随机字符串，包含大小写字母和数字
     * @param length 字符串长度，必须大于0
     * @return 随机字符串
     */
    public static String generateRandomString(int length) {
        return generateRandomString(length, true, true, true, false);
    }

    /**
     * 生成指定长度的随机字符串，可指定包含的字符类型
     * @param length 字符串长度，必须大于0
     * @param includeUpperCase 是否包含大写字母
     * @param includeLowerCase 是否包含小写字母
     * @param includeDigits 是否包含数字
     * @param includeSpecial 是否包含特殊符号
     * @return 随机字符串
     */
    public static String generateRandomString(int length, 
                                             boolean includeUpperCase, 
                                             boolean includeLowerCase, 
                                             boolean includeDigits, 
                                             boolean includeSpecial) {
        // 验证长度合法性
        if (length <= 0) {
            throw new IllegalArgumentException("字符串长度必须大于0");
        }
        
        // 构建字符源
        StringBuilder charPool = new StringBuilder();
        if (includeUpperCase) {
            charPool.append(UPPER_CASE);
        }
        if (includeLowerCase) {
            charPool.append(LOWER_CASE);
        }
        if (includeDigits) {
            charPool.append(DIGITS);
        }
        if (includeSpecial) {
            charPool.append(SPECIAL_CHARS);
        }
        
        // 验证字符源不为空
        if (charPool.length() == 0) {
            throw new IllegalArgumentException("至少要包含一种字符类型");
        }
        
        // 生成随机字符串
        StringBuilder result = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            // 从字符池中随机选取一个字符
            int index = RANDOM.nextInt(charPool.length());
            result.append(charPool.charAt(index));
        }
        
        return result.toString();
    }

    /**
     * 生成指定长度的盐值（包含大小写字母、数字和特殊符号，安全性更高）
     * @param length 盐值长度，建议8-16位
     * @return 盐值字符串
     */
    public static String generateSalt(int length) {
        return generateRandomString(length, true, true, true, true);
    }

    /**
     * 生成指定长度的验证码（仅包含数字和大写字母，易于识别）
     * @param length 验证码长度，通常4-6位
     * @return 验证码字符串
     */
    public static String generateVerificationCode(int length) {
        return generateRandomString(length, true, false, true, false);
    }

    /**
     * 生成随机密码（至少包含三种字符类型，符合密码强度要求）
     * @param length 密码长度，建议至少8位
     * @return 随机密码
     */
    public static String generateRandomPassword(int length) {
        if (length < 8) {
            throw new IllegalArgumentException("密码长度建议至少8位");
        }
        
        // 确保至少包含三种字符类型
        List<Character> passwordChars = new ArrayList<>(length);
        
        // 至少添加一个大写字母
        passwordChars.add(UPPER_CASE.charAt(RANDOM.nextInt(UPPER_CASE.length())));
        // 至少添加一个小写字母
        passwordChars.add(LOWER_CASE.charAt(RANDOM.nextInt(LOWER_CASE.length())));
        // 至少添加一个数字
        passwordChars.add(DIGITS.charAt(RANDOM.nextInt(DIGITS.length())));
        
        // 剩余字符随机生成（可以包含特殊符号）
        String remainingChars = generateRandomString(length - 3, true, true, true, true);
        for (char c : remainingChars.toCharArray()) {
            passwordChars.add(c);
        }
        
        // 打乱顺序
        StringBuilder password = new StringBuilder(length);
        while (!passwordChars.isEmpty()) {
            int index = RANDOM.nextInt(passwordChars.size());
            password.append(passwordChars.remove(index));
        }
        
        return password.toString();
    }
}
